Part Number Hot Search : 
4ALVCH16 10M25 7812S 64T1280 SM150 B4179 L15PF 1300000
Product Description
Full Text Search
 

To Download AN113 Datasheet File

  If you can't view the Datasheet, Please click here to try to view without PDF Reader .  
 
 


  Datasheet File OCR Text:
  rev. 1.3 12/03 copyright ? 2003 by silicon laboratories AN113-ds13 AN113 s erial c ommunication with the smb us relevant devices this application note applies to the following devices: c8051f000, c8051f001, c8051f002, c8051f005, c8051f006, c8051f010, c8051f011, c8051f012, c8051f020, c8051f021, c8051f022, and c8051f023. introduction c8051f0xx devices are equipped with an smbus serial i/o device that is compliant with the system management bus specificat ion version 1.1, as well as the i 2 c serial bus. the sm bus is a bi-direc- tional, 2-wire interface capable of communication with multiple devices. smbus is a trademark of intel; i 2 c is a trademark of philips semiconductor. this application note desc ribes configuration and operation of the smbus. example assembly and c code is given: (1) in terfacing a single eeprom with 1-byte address space, in assembly; (2) inter- facing multiple eeproms with 2-byte address space, in c; and (3) peer-to-peer communication between two c8051f0xx devices, in c. smbus specification this section presents a description of the smbus protocol. the smbus discu ssion begins in the next section--using the smbus. smbus structure an smbus system is a 2- wire network, where each device has a unique addres s and may be addressed by any other device on th e network. all transfers are initiated by a master device; if a device recog- nizes its own address and responds, it becomes the slave device for that transfer . it is important to note that assigning one specifi ed master device is not necessary. any device may a ssume the role of mas- ter or slave for any particular transfer. in the case that two devices attempt to initiate a transfer simul- taneously, an arbitration scheme forces one device to give up the bus. this arbitration scheme is non- destructive (one device wi ns and no information is lost). arbitration is discu ssed in depth in the arbi- tration section. two wires are used in smbus communication: sda (serial data), and scl (serial clock). each line is bi-directional, with direction depending on what modes the devices ar e in. the master always supplies scl; either devi ce may transmit on sda. both lines should be conn ected to a pos itive power supply through a pull-up circ uit. all devices on the smbus line should have an open-drain or open col- lector output, so that the lines may remain high when the bus is free. the line is pulled low if one or more devices attempts to output a low signal. all devices must output a high for the line to stay high. a typical smbus conf iguration is shown in figure 1 on page 2.
AN113 2 rev. 1.3 handshaking smbus employs various li ne conditions as hand- shaking between devices. note that during a data transfer, sda is only al lowed to change levels while scl is low. changes on sda while scl is high represent start and stop signals, as fol- lows: start : this initiates a transfer. it consists of a falling edge on sda while scl is high. stop : this ends a transfer. it consists of a rising edge on sda while scl is high. acknowledge : also referred to as an ack, this is transmitted by a receiving device as a confir- mation. for example, after device_x receives a byte, it transmits an ack to confirm the transfer. an ack consists of a low level on sda sampled when scl is high. not_acknowledge : also referred to as a nack, this is a high sda while scl is high. when a receiving device fails to ack, the sending device sees a nack. in typical transfers, a received nack indicates th at the addressed slave is not ready for transfer, or is not present on the bus. a receiving master may transmit a nack to indicate the last byte of a tr ansfer. both of these sit- uations are discussed furt her in the next section. figure 2 illustrates the handshaking signals. transfer modes two types of transfers are possible: a write (transfer from master to sl ave) and a read (trans- fer from slave to master ). during a transfer, any device may assume one of four roles. these four roles are explained below. note that ?slave address + r/w? refers to an 8 bit transfer (7 address, 1 r/ w). 1) master transmitter: in this mode, the device transmits serial data on sda and drives the clock on scl. the device initia tes the transfer with a start condition, sends the slave address + w, and waits for an ack from the slave. after the ack, figure 1. typical sm bus configuration vdd = +5v/+3v device 1 device 2 device 3 sda scl sla6 sda sla5-0 r/w d7 d6-0 scl slave address + r/w data byte start ack nack stop figure 2. smbus timing
AN113 rev. 1.3 3 the device transmits one or more bytes of data, with each byte ack?ed by the sl ave. after the last byte, the device transmits a stop. 2) master receiver: in th is role, the device receives serial data on sd a while driving the clock on scl. the device initiates the tr ansfer with a start fol- lowed by the slave addre ss + r. after the slave ack?s the address, the de vice will output the clock on scl, and receive data on sda. after the last byte, the device will issue a nack followed by a stop. 3) slave transmitter: in this role, a device outputs serial data on sda and receives the clock on scl. the device receives a st art followed by its own slave address + r, then ack?s, and enters slave transmitter mode. the devi ce transmits serial data on sda and receives an ac k after each byte. after the last byte, the master will issue a nack fol- lowed by a stop. 4) slave receiver: in this role, a device receives a start followed by its own slave address + w from a master device. th e device sends an ack and enters slave receive r mode. the device now receives serial data on sda and the clock on scl. the device ack?s after each byte is received, and exits slave mode after the master issues a stop. figure 3 shows the typical write scenarios. (1) shows a successful transfer. in (2), the master receives a nack after sending the slave address + w. this occurs when a slave is ?offline?, meaning it is not responding to its own address. in this case, the master should issue a stop or repeated start. to retry the transfer, the master follows the stop with a start and the slave address + w again. the master will repeat the figure 3. typical write transfer scenarios from slave to master nack received after sla + w p a (2) (3) repeat start issued after acknowledge a sla + r s (4) nack received after data p a s = start sla = slave address (7 bits) w = write (1 bit) r = read (1 bit) data = serial data (8 bits) a = acknowledge a = not-acknowledge p = stop successful write s sla + w a data p a a data (1) any number of data bytes and acknowledges from master to slave data
AN113 4 rev. 1.3 cycle until it receives an ac k. this is referred to as ?acknowledge polling?. in (3), the master issues a repeated start after an ack. this process allows the master to initiate a new transfer without gi ving up the bus (to switch from a write to a read, for example). the repeated start is commonly used in eeprom memory access applications, where a memory read must be directly preceded by a write of the desired memory location. the repeated start is demonstrated in a ll three code examples. in (4), a nack is received after a data byte. in typ- ical smbus systems, this is how the receiving device indicates an error. the master sends a stop, and retries the transfer as in (2), or gives up the transfer. note that th e use of nacks is not restricted to error situat ions; the acknowledge level is a user-definable characteristic, and may vary in different applications. figure 4 shows the typical read scenarios. (1) shows a successful read operation. in (2), the master receives a nack after sending the slave address + r. this situation is handled in the same fashion as in (2) of the write discussion. the master can use acknowledge polling to retry the transfer, or it can give up the transfer. (3) shows the master sending a repeated start after sending a byte of data. this is the same repeated start state as in the write discu ssion. a master may send a repeated start after any data byte, and may ini- tiate a read or a writ e following the repeated start. generally a repeated start is used to change direction (r/w) or to change addresses (slave devices). note that the read and write diagrams show only the typical scenarios. bus errors, time outs, and arbitration are also possible occurrences. time- outs are used to detect when a transfer has stalled or when the bus is free. often a device may hold scl low until it is ready to continue a transfer. this process allows a slower slave device to communi- cate with a faster mast er, since stalling the bus effectively reduces the scl frequency. the smbus protocol specifies that all devices on the smbus figure 4. typical read scenarios s = start sla = slave address (7 bits) w = write (1 bit) r = read (1 bit) data = serial data (8 bits) a = acknowledge a = not-acknowledge p = stop from slave to master any number of data bytes and acknowledges from master to slave data nack received after sla + r p a (2) (3) repeat start issued after ack a sla + r s successful read (1) s sla + r a data p a a data
AN113 rev. 1.3 5 must declare any scl si gnal held low for more than 25 ms a ?timeout?. in this case, all devices on the bus must reset communication. a high scl timeout may also occur. if both sda and scl remain high for more than 50 sec, the bus is des- ignated as free. arbitration if multiple masters are configured on the same smbus system, it is possibl e that two will attempt to initiate a transfer at the same time. if this hap- pens, an arbitration sche me is employed to force one device to give up the bus. what the scheme is: both ma sters continue to trans- mit until one attempts a high while the other attempts a low. due to the open-drain bus, the device attempting a low will win the bus. the high device gives up the bus , and the other device continues its transfer. note that the collision is non- destructive: one device always wins. how it works: assume device_x and device_y contend for the bus. the winner, device_x, is not affected at all by the ar bitration. since data is shifted into the smbus data register as it is shifted out, device_y does not miss any data. figure 5 shows an example output sequence between two devices during arbitrati on. note that device_y begins receiving data af ter it gives up the bus. using the smbus the smbus can operate in both master and slave modes. the hardware pr ovides timing and shifting control for the serial tran sfers; byte-wise control is user-defined. the smbus hardware performs the following application- independent tasks: timing control: in master mode, the hardware gen- erates the clock signal on scl and synchronizes the data on sda. hardware also recognizes time- outs and bus errors. serial data transfers: the hardware controls all shifting of data to and from sda, including the acknowledge level. the ac knowledge level is user- defined, as explained in the register definitions below. slave address recognition: the hardware recog- nizes a start from anothe r device, and reads the following slave address. if the slave address matches the contents of the smbus address regis- ter (defined below), then the hardware acknowl- edges the address. note th at this features is only enabled if aa (address acknowledge) is set. configuration and control smbus operation is determined by the contents of the following registers. figure 5. arbitration sequence device_y device_x 0 1 1 101 10 0 1111 0 1 1 101 10 seen on the bus device_y gives up the bus
AN113 6 rev. 1.3 smb0sta. the smbus status register holds an 8-bit status code for the current state of the smbus. the contents of smb0st a are only defined when the si bit is set. there are 28 possible states, all of which have a unique code (the codes are multiples of 8). smb0sta should ne ver be written to. the 28 possible states and thei r descriptions are given in table 1 on page 12. smb0cn. the smbus control re gister is used to enable the smbus and navigate the possible smbus states. this register includes start and stop control, as well as interrupt, acknowledge, and timeout control. a transfer is initiated by setting the sta bit. the smbus hardware will wait until the bus is free, then transmit a start. note that sta is not cleared by hardware. user software must manu- ally clear sta so that an unwanted repeated start is not generated. user software must also manually clear sto prior to setting sta. a transfer is ended by sett ing the sto bit. in mas- ter mode, setting sto will cause a stop condition to be generated. if sta is set when sto is set, a stop followed by a start will be transmitted. in slave mode, setting sto will cause the hardware to act as if a stop wa s received, though no stop condition is transmitted. the si bit is set when any of the possible 28 smbus states are entered (e xcluding the idle state). this bit is not automatically cleared by hardware. note that scl is held low while si is set. this means that the bus is stalle d until si is cleared, syn- chronizing the master with the slave. the aa bit determines the type of acknowledge returned during the acknowledge cycle. if aa=1, an ack will be sent; if aa=0, a nack will be sent. this means the device will respond to its slave address only if aa is set. scl high and low timeout detection is enabled by setting the fte and toe bits, respectively. the smbus is enabled by setting the smbus enable bit, ensmb. smb0cr. the smbus clock regi ster is used to control the scl clock rate when the device is in master mode. the 8 bits held in the smb0cr reg- ister determine the clock rate as follows: <1> where smb0cr is a 2? s complement negative number. so for a scl fr equency of 100 khz and a sysclk of 16 mhz, smb0cl should be loaded with -80, or 0xb0. smb0cr also defines th e limit for the bus free time period (high scl time out). the bus free time is defined by the following equation, where smb0cr is a 2?s comple ment negative number. note that t free is about 5 bit periods. <2> smb0adr. the smbus address register holds the slave address that the device will respond to in slave mode. bits(7:1) hold the slave address; bit0 is the general call enable. if bit0 is set, the device will respond to the gene ral call addr ess (0x00). smb0dat . the smbus data register is used to hold data to be transmitte d or data that has just been received by the smbus. data read from this register is only valid whil e si = 1. when si is not set, the smbus may be in the process of shifting data in or out of smb0da t. note that when trans- mitting, data shifted out of the most significant bit of smb0dat is shifted back into the least signifi- cant bit, so that after a tr ansmit the original data is still contained in smb0dat. smb0cr sysclk 2f scl ------------ ------------ - ? ? t free 10 smb0cr () 1 + sysclk ------------------- ----------------- ----------------- - ? =
AN113 rev. 1.3 7 implementation choices user software controls the smbus on a state-by- state basis. upon each state change, the si bit is set by hardware, and an interrupt generated if inter- rupts are enabled. the smbu s is then halted until user software services th e state change and clears the si bit. the smbus operation is most easily defined in a state table; however, note that it is not necessary to define all 28 st ates. for example, if the smbus is the only master in the system, the slave and arbitration states may be left undefined. if the smbus will never operate as a master, the master states may be left undefined. if states are left unde- fined, a default response should be programmed to account for unexpected or error situations. the smbus state table lends itself to a case-switch statement definition in c. however, for simple or time-restricted systems, an assembly state decoding can be more efficient. note that the status codes held in smb0sta are multipl es of 8. if the smbus states are programmed in 8-byte segments, smb0sta may be used as a software index. in this case, a status code is decoded in 3 assembly com- mands. however, only 8 byt es of code space are available for each state definition. for states that require more than 8 bytes, the program must branch out of the state table so th at subsequent states are not disturbed. examples three examples are pr ovided: a single eeprom with 1-byte address space, in assembly; multiple eeproms with 2-byte addre ss space, in c; and a peer-to-peer interface be tween two devices, in c. each example uses interrupt-driven operation. single eeprom this is a simple interface between the smbus and a 256-byte eeprom. the smbus acts as the master at all times. the transfer procedure is similar to that of any 2-wire eeprom interface. the send operation is a 1-byte random write. the smbus sends a start followed by three bytes: the eeprom?s device address + w (this address is found in the eeprom datasheet), the memory location to be wr itten, and then the data byte. the slave should ack after each byte. if the master receives an ack af ter each byte, it sends a stop and the transfer is over. if at any time the master receives a nack, it will retry the transfer using acknowledge polling. it is common for an eeprom to nack if multiple read/write opera- tions are performed sequentially, since most self- timed eeproms go offline to actually perform the memory write. figure 6 shows sda for the single eeprom send operation. the receive operation is a 1-byte random read. the transfer begins, as in the write function, with the master sending a start followed by the eeprom device address + w (a write is used to set the eeprom?s ?current address?). after the slave ack?s, the master sends the memory location to be read. upon receipt of an ack, the master then issues a repeated start followed by the slave address + r. now after the slave ack?s, it will send the data byte read from the location given in the preceding ?aborte d? write. the master sends a nack (since this data is the last and only byte), followed by a stop. the repeated start is used in this case so th at no other transfers may begin between the write of the memory address and the read of the data byte. figure 7 shows sda for a single eeprom receive operation. the software for this example was written in assembly to demonstrate the advantage of using smb0sta as a software index. the smbus state table written in 8-byte memory segments (8 bytes for each state). this is accomplished through the use of an ?org? statement fo r each state, offset from the beginning of the tabl e by the corresponding sta- s sla wa a a p 8-bit address data byte figure 6. single eerpom send sequence
AN113 8 rev. 1.3 tus code. for example, if the state table is labeled state_table, and state _1 is 0x08, the code seg- ment for state_1 should begin with: ; state_1 org state_table + 08h ; state_1 code now when smb0sta holds 0x80, state_1 may be accessed with the following: ; load current state mov a, smb0sta; ; point dptr to start of table mov dptr, #state_table; ; jump to indexed state jmp @a+dptr; this process allows for ve ry efficient state decod- ing. however, it is importa nt to note that only 8 bytes of code space are avai lable for each state. if a state requires more than 8 bytes, the program must jump to a segment outside of the state table, so that the next state defini tion is not disturbed. to keep the states simple and understandable, the smbus is assumed to be the only master in the sys- tem. the slave states are not defined, and the arbi- tration states ignore any received data. also, the repeated start state may as sume the transfer is a read. the code listing begins on page 14. . figure 7. single eepr om receive sequence a s sla wa 8-bit address s sla ra data byte n p s sla w a a high address byte a low address byte s sla ra data byte n p figure 8. multiple ee prom receive sequence
AN113 rev. 1.3 9 multiple eeproms example 2 uses multiple eeproms with 2-byte address space. the software is written in c. the three eeproms used are 8k-bytes. note that three identical eeproms are used. the eeproms have three address selection pins , a0 - a2, that are used to set the slave address for the devices. the four high bits of the device address are set in eeprom to ?0101?; the lower three bits of the slave address are determined by the set ting of the address pins (vdd for 1, gnd for 0). fi gure 9 shows the device configuration. the distinction with this example is that the eeproms have a 2-byte address space. this means that the read a nd write operations must send an extra address byte for each transfer (see figure 8) when the interrupt service routine reaches the ?data tran smitted, ack received? state, it must know whic h byte was transmitted--the high address byte, the low address byte, or the data byte. this information is kept in the byte_number state variable. the smbus isr is implemented as a case-switch statement, with th e smbus status code (smb0sta) used as the switch variable. the code listing for this example begins on page 23. figure 9. multiple eeprom configuration chip_a a2 a1 a0 chip_c a2 a0 a1 chip_b a2 a1 vdd vdd sda scl a0 cf000 addr = 001 addr = 010 vdd 2.7k addr = 000 2.7k
AN113 10 rev. 1.3 peer-to-peer interface the final example featur es two c8051f0xx devices configured to communicate as peers. the peer-to- peer interface uses a set of op codes to perform the set of tasks below. either device may initiate a transfer. write to slave dac: the master de vice sends a write_dac op code follow ed by a byte of data. upon receipt, the slave device writes the data to its dac0 port. write to buffer : the master device sends a write_buf op code, followed by a byte of data for the receiving device to store in a buffer. the upper 4 bits of the write_buf op code hold the buffer index. figure 10 shows a peer-to-peer write sequence (same for both dac and buffer writes). read adc: the master device sends a read_adc op code fo llowed by a repeated start. the slave reads its adc input, and places the data in its smb0dat re gister. in this case, the slave clears aa to go ?o ffline? during the adc conversion. while the slave is offline, the master receives a nack after the repeated start and slave address. the master continues acknowledge polling until the slave responds. this technique is useful if the slave?s ope ration is time-consuming, since other devices may us e the bus while the slave is offline. the slave sets aa=1 when it is ready, and the transfer continue s. the master requests a read after the slave ac knowledges. see figure 11 for the transmission sequence. read buffer: the master sends a read_buf op code followed by a repeated start. the upper 4 bits of the op code hold the buffer index. in this case the slave holds the scl line low while it decodes the op code. while scl is held low, the master cannot attempt to continue the transfer. additionally, no other masters on the bus may attempt a transfer. this bus stalling technique is useful when the slave?s de lay is short. the slave releases scl when it has finished decoding the op code and is ready to tran smit the data. the master issues the repeated start and the slave address + r. see figure 11. the smbus operation in this example is defined as a case-switch statement in the smbus isr. all pos- sible states are defined, including the arbitration states. if arbitration occurs, the losing device stores s sla wa write op code a p data byte a bus stalled here until slave decodes the op code figure 10. peer-to-peer write sequence figure 11. peer-to-p eer read sequence s sla wa read_buf op code s sla ra data byte n p a bus stalled here until slave decodes the op code a s sla wa read adc op code slave goes 'offline' here until adc conversion is complete. s sla r a data byte n p buffer read adc read
AN113 rev. 1.3 11 its current transfer data (target slave address, op code, relevant data) and re sponds to the received op code. after the transfer is finished, the losing device retries the transfer by reverting to the saved transfer data. an op_code_handler f unction runs in polled mode to process received data. when the device receives a valid op code, the op_code_handler dec odes it and reacts appropriately. to test the bus, comment out the op_code_handler call in the code for chip_a. this will allow chip_a to run the pro- vided test code. note that the constant my_add must be unique to each device on the bus. the code listing for this example begins on page 29.
AN113 12 rev. 1.3 table 1. smbus status codes and states mode status code smbus state typical action mt/ mr 0x08 start condition transmitted. load smb0dat with slave address + r/w 0x10 repeated start condition transmitted. load smb0dat with slave address + r/w master transmitter 0x18 slave address + w transmitted. ack received. load smb0dat with data to be transmit- ted. clear sta 0x20 slave address + w transmitted. nack received. acknowledge poll to retry. set sto + sta 0x28 data byte transmitt ed. ack received. 1) load smb0dat with next byte, or 2) set sto, or 3) clear sto, then set sta for repeated start 0x30 data byte transmitted. nack received. 1) retry transfer or 2) set sto 0x38 arbitration lost. save current data master receiver 0x40 slave address + r transmitted. ack received. clear sta. wait for received data. 0x48 slave address + r transmitted. nack received. acknowledge poll to retry. set sto + sta 0x50 data byte received. ack transmitted. read smb0dat. wait for next byte. if next byte is last byte, clear aa 0x58 data byte received. nack transmitted. set sto
AN113 rev. 1.3 13 slave receiver 0x60 own slave address + w received. ack trans- mitted. wait for data 0x68 arbitration lost in sending sla + r/w as mas- ter. own address + w received. ack transmit- ted. save current data for retry when bus is free. wait for data 0x70 general call address re ceived. ack transmit- ted. wait for data 0x78 arbitration lost in sending sla + r/w as mas- ter. general call address received. ack trans- mitted. save current data for retry when bus is free. 0x80 data byte received. ack transmitted. read smb0dat. wait for next byte or stop 0x88 data byte received. nack transmitted. set sto to reset smbus 0x90 data byte received after general call address. ack transmitted. read smb0dat. wait for next byte or stop 0x98 data byte received after general call address. nack transmitted. set sto to reset smbus 0xa0 stop or repeated start received. no action necessary slave transmitter 0xa8 own address + r received. ack transmitted. load smb0dat with data to transmit. 0xb0 arbitration lost in transmitting sla + r/w as master. own address + r received. ack transmitted. save current data for retry when bus is free. load smb0dat with data to trans- mit. 0xb8 data byte transmitt ed. ack received. load smb0dat with data to transmit. 0xc0 data byte transmitted. nack received. wait for stop 0xc8 last data byte transmitted (aa=0). ack received. set sto to reset smbus slave 0xd0 scl clock high timer per smb0cr timed out set sto to reset smbus all 0x00 bus error (illegal start or stop) set sto to reset smbus 0xf8 idle state does not set si table 1. smbus status codes and states mode status code smbus state typical action
AN113 14 rev. 1.3 software examples for th e c8051f00x and c8051f01x series ;--------------------------------------------------------------------------------- ; ; copyright 2001 cygnal integrated products, inc. ; ; program: smbus_ex1.asm ; created on: 2/21/01 ; last mod : 27 aug 03 -- bw ; created by: js ; ; example code to interface a single 256-byte eeprom to a c8051f00x via the smbus ; code assumes a single eeprom with slave address 1010000 is connected on ; the sda and scl lines, and no other masters are on the bus. ; ; the send routine performs a 1-byte write to the eeprom. this consists of (1) start, ; (2) slave address + w, (3) memory location byte write, and (4) a data byte write. ; ; steps for writing to eeprom: ; 1) load slave address into sla_add ; 2) load memory address into mem_add ; 3) load data byte into transmit_byte. ; 4) call send ; ; the receive routine performs a 1-byte read from the eeprom. this consists of (1) ; start, (2) slave address + w, (3) memory location byte write, (4) repeated start, ; (5) slave address + r, (6) data byte read. ; ; steps for receiving data: ; 1) load slave address into sla_add ; 2) load memory address into mem_add ; 3) call receive ; 4) read receive_byte ; ; the smbus state table is broken into 8-byte state segments, allowing the smbus ; status code (smb0sta) to be used as a state index. note that this leaves only ; 8 bytes of code space per smbus state definition. as a result, certain tasks ; have been altered to limit state definition lengths: ; ; 1) the smb_mtdback state (master transmitter, data byte sent, ack received) is ; reduced to a bit-check and branch operation. the branch is outside of the state ; table, so that a larger code segment may be executed for this state. ; ; 2) three data bytes are used for slave address storage: sla_add, wri_add, read_add. ; rather than using bit-wise operations in the smbus states, each transfer routine ; pre-loads the address values. since a receive includes both a write and read ; transfer, two address bytes are necessary - wri_add and read_add. sla_add is used ; as a generic slave chip select before a function call. ; ; note that sla_add is equivalent to wri_add, since wri_add = sla_add + w (w=0). ; the two are left separate to clarify the demonstration. ; ;-----------------------------------------------------------------------------------
AN113 rev. 1.3 15 ;----------------------------------------------------------------------------------- ; equates ;----------------------------------------------------------------------------------- $include (c8051f000.inc) ; include register definition file. write equ 00h ; smbus write command read equ 01h ; smbus read command chip_a equ 0a0h ; eeprom slave address ; smbus states smb_bus_error equ 00h ; (all modes) bus error smb_start equ 08h ; (mt & mr) start transmitted smb_rp_start equ 10h ; (mt & mr) repeated start smb_mtaddack equ 18h ; (mt) slave address + w transmitted; ; ack received smb_mtaddnack equ 20h ; (mt) slave address + w transmitted; ; nack received smb_mtdback equ 28h ; (mt) data byte transmitted; ack rec?vd smb_mtdbnack equ 30h ; (mt) data byte transmitted; nack rec?vd smb_mtarblost equ 38h ; (mt) arbitration lost smb_mraddack equ 40h ; (mr) slave address + r transmitted; ; ack received smb_mraddnack equ 48h ; (mr) slave address + r transmitted; ; nack received smb_mrdback equ 50h ; (mr) data byte rec?vd; ack transmitted smb_mrdbnack equ 58h ; (mr) data byte rec?vd; nack transmitted ;----------------------------------------------------------------------------------- ; variables ;----------------------------------------------------------------------------------- mydata segment data ; declare data segment rseg mydata ; select data segment transmit_byte: ds 1 ; holds a byte to be transmitted by the smbus receive_byte: ds 1 ; holds a byte just received by the smbus sla_add: ds 1 ; holds the slave address wri_add: ds 1 ; holds the slave address + write read_add: ds 1 ; holds the slave address + read mem_add: ds 1 ; eeprom memory location to be accessed ; variables used for testing. test_count: ds 1 ; test counter variable test_byte: ds 1 ; test data test_addr: ds 1 ; test memory location mybits segment bit rseg mybits rw: dbit 1 ; r/w command bit. 1=read, 0=write sm_busy: dbit 1 ; smbus busy flag (kept in software) byte_sent: dbit 1 ; used to indicate what byte was just sent: ; 1: eeprom memory address sent ; 0: data byte sent
AN113 16 rev. 1.3 ;------------------- ; stack stack segment idata ; declare stack segment rseg stack ds 80h ; reserve 128 bytes for stack ;------------------------------------------------------------------------------------ ; reset and interrupt vectors ;------------------------------------------------------------------------------------ cseg ; reset vector org 00h ljmp reset_vector ; smbus interrupt vector org 03bh ljmp smbus_isr mycode segment code rseg mycode using 0 ;-------------------------------------------------------------------------------------- ; reset vector ; ; - disables watchdog timer ; - routes sda and scl to gpio pins via the crossbar ; - enables crossbar ; - jumps to main reset_vector: mov wdtcn, #0deh ; disable watchdog timer mov wdtcn, #0adh mov sp, #stack ; initialize stack pointer orl oscicn, #03h ; set internal oscillator to highest setting ; (16 mhz) mov xbr0, #01h ; route smbus to gpio pins through crossbar mov xbr2, #40h ; enable crossbar and weak pull-ups ljmp main ;------------------------------------------------------------------------------------ ; main program ;------------------------------------------------------------------------------------ main: acall smbus_init ; initialize smbus setb ea ; enable global interrupts mov test_byte, #0ffh ;
AN113 rev. 1.3 17 mov test_addr, #00h ; load initial test values mov test_count, #0feh ; ; test code-------------------------------------------------------------------------- test: ; send test_byte to memory location test_addr mov sla_add, #chip_a ; load slave address mov transmit_byte, test_byte ; load transmit data into transmit_byte mov mem_add, test_addr ; load memory address into mem_add acall send ; call send routine ; read memory location test_addr into receive_byte mov sla_add, #chip_a ; load slave address mov mem_add, test_addr ; load memory address into mem_add acall receive ; call receive routine ; compare byte received to byte sent mov a, receive_byte ; load received byte into accumulator cjne a, test_byte, end_test ; compare sent byte to received byte ; jump to end_test if not equal ; change test variables dec test_byte ; if sent=received, change test variables inc test_addr ; and cycle through again. ; cycle through again if test_counter not zero djnz test_count, test ; decrement counter, loop back to beginning mov a, #99h ; load accumulator with 99h if test successful. end_test: jmp $ ; spin ;--------------------------------------------------------------------------------------- ; subroutines ;--------------------------------------------------------------------------------------- ;--------------------------------------------------------------------------------------- ; send subroutine. assumes that the slave address, memory location, and transmit ; data have all been loaded into their associated variables. this routine manages ; the sm_busy bit, sets rw=write, loads the wri_add, and initiates the transfer. ; send: push acc ; preserve accumulator jb sm_busy, $ ; wait for smbus to be free clr rw ; rw = 0 (write) mov a, sla_add ; store sla_add + write orl a, #write ; in wri_add mov wri_add, a ; setb sm_busy ; occupy smbus setb sta ; initiate transfer pop acc ; restore accumulator
AN113 18 rev. 1.3 ret ;--------------------------------------------------------------------------------------- ; receive subroutine. assumes that the slave address and memory location have been ; loaded into their associated variables. this routine manages the sm_busy bit, sets ; rw=read, loads the read_add and wri_add, and initiates the transfer. ; ; note that the receive transfer consists of a write of the memory location to be accessed, ; followed by a repeated start and a read operation. therefore, both wri_add ; and read_add are used by this routine. receive: push acc ; preserve accumulator jb sm_busy, $ ; wait for smbus to be free setb rw ; rw = 1 (read) mov a, sla_add ; store sla_add + write orl a, #write ; in write_add mov wri_add, a ; mov a, sla_add ; store sla_add + read orl a, #read ; in read_add mov read_add, a ; setb sm_busy ; occupy smbus setb sta ; initiate transfer jb sm_busy, $ ; wait for receive to finish pop acc ; restore accumulator ret ;--------------------------------------------------------------------------------------- ; smbus_init ; smbus initialization routine ; ; - configures and enables the smbus. ; - sets smbus clock rate. ; - enables smbus interrupt. ; - clears sm_busy flag for first transfer. smbus_init: mov smb0cn, #04h ; configure smbus to send acks on acknowledge cycle mov smb0cr, #0b0h ; smbus clock rate = 100khz, per smb0cr equation: ; smb0cr = -(sysclk)/(2*fscl) orl smb0cn, #40h ; enable smbus orl eie1, #02h ; enable smbus interrupts clr sm_busy ret ;-------------------------------------------------------------------------------------- ; interrupt vectors ;--------------------------------------------------------------------------------------
AN113 rev. 1.3 19 ;-------------------------------------------------------------------------------------- ; smbus isr ; ; implemented as a state table lookup, with the smbus status register as the index. ; smbus status codes are multiples of 8; thus the status code can be used to index ; program segments that are spaced by 8 bytes. each ?org? command indicates ; a new state, offset from the beginning of the table by its status code value. ; ; note that only 8 bytes are available to process each state. in the cases where ; more than 8 bytes are necessary, the code jumps to a program location outside ; of the state table. this is only necessary in the state ?smb_mtdback?. smbus_isr: push psw ; push acc ; push dph ; resource preservation push dpl ; push acc ; mov a, smb0sta ; load accumulator with current smbus state. ; state corresponds to the address offset ; for each state execution anl a, #7fh ; mask out upper bit, since any states that ; set this bit are not defined in this code. mov dptr, #smb_state_table ; point dptr to the beginning of the state table jmp @a+dptr ; jump to the current state ; smbus state table------------------------------------------------------------------------ smb_state_table: ; smb_bus_error ; all modes: bus error ; reset hardware by setting stop bit org smb_state_table + smb_bus_error setb sto jmp smb_isr_end ; jump to exit isr ; smb_start ; master transmitter/receiver: start transmitted. ; the r/w bit will always be a zero (w) in this state because ; for both write and read, the memory address must first be written. org smb_state_table + smb_start mov smb0dat, wri_add ; load slave address + w clr sta ; manually clear start bit jmp smb_isr_end ; jump to exit isr ; smb_rp_start ; master transmitter/receiver: repeated start transmitted. ; this state should only occur during a read, after the memory ; address has been sent and acknowledged. org smb_state_table + smb_rp_start
AN113 20 rev. 1.3 mov smb0dat, read_add ; load slave address + r clr sta ; manually clear start bit jmp smb_isr_end ; smb_mtaddack ; master transmitter: slave address + write transmitted. ; ack received org smb_state_table + smb_mtaddack mov smb0dat, mem_add ; load memory address setb byte_sent ; byte_sent=1: in the next isr call, ; the memory address will have just been ; sent. jmp smb_isr_end ; smb_mtaddnack ; master transmitter: slave address + write transmitted. ; nack received. the slave is not responding. try again with ; acknowledge polling. send stop + start. org smb_state_table + smb_mtaddnack setb sto setb sta jmp smb_isr_end ; smb_mtdback ; master transmitter: data byte transmitted. ack received. ; this state is used in both read and write operations. ; check byte_sent; if 1, memory address has just been sent. else, ; data has been sent. org smb_state_table + smb_mtdback jbc byte_sent, address_sent ; if byte_sent=1, clear bit and ; jump to address_sent to process ; outside of state table. jmp data_sent ; if byte_sent=0, data has just been sent, ; transfer is finished. ; jump to end transfer ; smb_mtdbnack ; master transmitter: data byte transmitted. nack received. ; slave not responding. send stop followed by start to try again. org smb_state_table + smb_mtdbnack setb sto setb sta jmp smb_isr_end ; smb_mtarblost ; master transmitter: arbitration lost. ; should not occur. if so, restart transfer. org smb_state_table + smb_mtarblost setb sto setb sta jmp smb_isr_end
AN113 rev. 1.3 21 ; smb_mraddack ; master receiver: slave address + read transmitted. ack received. ; set to transmit nack after next transfer since it will be the ; last (only) byte. org smb_state_table + smb_mraddack clr aa ; nack sent on acknowledge cycle jmp smb_isr_end ; smb_mraddnack ; master receiver: slave address + read transmitted. nack received. ; slave not responding. send repeated start to try again. org smb_state_table + smb_mraddnack clr sto setb sta jmp smb_isr_end ; smb_mrdback ; master receiver: data byte received. ack transmitted. ; should not occur because aa is cleared in previous state. ; send stop if state does occur. org smb_state_table + smb_mrdback setb sto jmp smb_isr_end ; smb_mrdbnack ; master receiver: data byte received. nack transmitted. ; read operation completed. read data register and send stop org smb_state_table + smb_mrdbnack mov receive_byte, smb0dat setb sto setb aa ; set aa for next transfer clr sm_busy jmp smb_isr_end ; end of state table-------------------------------------------------------------- ;--------------------------------------------------------------------------------- ; program segment to handle smbus states that require more than 8 bytes of program ; space. ; address byte has just been sent. check rw. if r (1), jump to rw_read. ; if w, load data to transmit into smb0dat. address_sent: jb rw, rw_read mov smb0dat, transmit_byte ; load data jmp smb_isr_end ; jump to exit isr ; operation is a read, and the address byte has just been sent. send ; repeated start to initiate memory read. rw_read: clr sto setb sta ; send repeated start jmp smb_isr_end ; jump to exit isr
AN113 22 rev. 1.3 ; operation is a write, and the data byte has just been sent. transfer ; is finished. send stop, free the bus, and exit the isr. data_sent: setb sto ; send stop and exit isr. clr sm_busy ; free smbus jmp smb_isr_end ; jump to exit isr ;--------------------------------------------------------------------------------- ; smbus isr exit. ; restore registers, clear si bit, and return from interrupt. smb_isr_end: clr si pop acc pop dpl pop dph pop acc pop psw reti end
AN113 rev. 1.3 23 //------------------------------------------------------------------------------------ // // copyright 2001 cygnal integrated products, inc. // // file name : smb_ex2.c // target device : c8051f000 // created on : 2/20/01 // created by : js // // // example code for interfacing a c8051f0xx to three eeproms via the smbus. // code assumes that three 16-bit address space eeproms are connected // on the scl and sda lines, and configured so that their slave addresses // are as follows: // chip_a = 1010000 // chip_b = 1010001 // chip_c = 1010010 // // slave and arbitration states are not defined. assume the cf000 is the only // master in the system. // functions: sm_send performs a 1-byte write to the specified eeprom // sm_receive performs a 1-byte read of the specified eeprom address (both include // memory address references). // // includes test code section. //------------------------------------------------------------------------------------ // includes //------------------------------------------------------------------------------------ #include // sfr declarations //------------------------------------------------------------------------------------ // global constants //------------------------------------------------------------------------------------ #define write 0x00 // smbus write command #define read 0x01 // smbus read command // device addresses (7 bits, lsb is a don?t care) #define chip_a 0xa0 // device address for chip a #define chip_b 0xa2 // device address for chip b #define chip_c 0xa4 // device address for chip c // smbus states: // mt = master transmitter // mr = master receiver #define smb_bus_error 0x00 // (all modes) bus error #define smb_start 0x08 // (mt & mr) start transmitted #define smb_rp_start 0x10 // (mt & mr) repeated start #define smb_mtaddack 0x18 // (mt) slave address + w transmitted; // ack received #define smb_mtaddnack 0x20 // (mt) slave address + w transmitted; // nack received #define smb_mtdback 0x28 // (mt) data byte transmitted; ack rec?vd #define smb_mtdbnack 0x30 // (mt) data byte transmitted; nack rec?vd #define smb_mtarblost 0x38 // (mt) arbitration lost #define smb_mraddack 0x40 // (mr) slave address + r transmitted; // ack received #define smb_mraddnack 0x48 // (mr) slave address + r transmitted;
AN113 24 rev. 1.3 // nack received #define smb_mrdback 0x50 // (mr) data byte rec?vd; ack transmitted #define smb_mrdbnack 0x58 // (mr) data byte rec?vd; nack transmitted //----------------------------------------------------------------------------------- //global variables //----------------------------------------------------------------------------------- char command; // holds the slave address + r/w bit for // use in the smbus isr. char word; // holds data to be transmitted by the smbus // or data that has just been received. char byte_number; // used by isr to check what data has just been // sent - high address byte, low byte, or data // byte unsigned char high_add, low_add; // high & low byte for eeprom memory address bit sm_busy; // this bit is set when a send or receive // is started. it is cleared by the // isr when the operation is finished. //------------------------------------------------------------------------------------ // function prototypes //------------------------------------------------------------------------------------ void smbus_isr (void); void sm_send (char chip_select, unsigned int byte_address, char out_byte); char sm_receive (char chip_select, unsigned int byte_address); //------------------------------------------------------------------------------------ // main routine //------------------------------------------------------------------------------------ // // main routine configures the crossbar and smbus, and tests // the smbus interface between the three eeproms void main (void) { unsigned char check; // used for testing purposes wdtcn = 0xde; // disable watchdog timer wdtcn = 0xad; oscicn |= 0x03; // set internal oscillator to highest setting // (16 mhz) xbr0 = 0x01; // route smbus to gpio pins through crossbar xbr2 = 0x40; // enable crossbar and weak pull-ups smb0cn = 0x44; // enable smbus with acks on acknowledge // cycle smb0cr = -80; // smbus clock rate = 100khz. eie1 |= 2; // smbus interrupt enable ea = 1; // global interrupt enable
AN113 rev. 1.3 25 sm_busy = 0; // free smbus for first transfer. // test code--------------------------------------------------------------------- sm_send(chip_a, 0x0088, 0x53); // send 0x53(data) to address 0x88 on chip_a sm_send(chip_b, 0x0001, 0x66); // send 0x66(data) to address 0x01 on chip_b sm_send(chip_c, 0x0010, 0x77); sm_send(chip_b, 0x0333, 0xf0); sm_send(chip_a, 0x0242, 0xf0); check = sm_receive(chip_a, 0x0088); // read address 0x88 on chip_a check = sm_receive(chip_b, 0x0001); // read address 0x01 on chip_b check = sm_receive(chip_c, 0x0010); check = sm_receive(chip_b, 0x0333); check = sm_receive(chip_a, 0x0242); // end test code----------------------------------------------------------------- } // smbus byte write function----------------------------------------------------- // writes a single byte at the specified memory location. // // out_byte = data byte to be written // byte_address = memory location to be written into (2 bytes) // chip_select = device address of eeprom chip to be written to void sm_send (char chip_select, unsigned int byte_address, char out_byte) { while (sm_busy); // wait for smbus to be free. sm_busy = 1; // occupy smbus (set to busy) smb0cn = 0x44; // smbus enabled, // ack on acknowledge cycle byte_number = 2; // 2 address bytes. command = (chip_select | write); // chip select + write high_add = ((byte_address >> 8) & 0x00ff);// upper 8 address bits low_add = (byte_address & 0x00ff); // lower 8 address bits word = out_byte; // data to be writen sto = 0; sta = 1; // start transfer } // smbus random read function------------------------------------------------------ // reads 1 byte from the specified memory location. // // byte_address = memory address of byte to read // chip_select = device address of eeprom to be read from char sm_receive (char chip_select, unsigned int byte_address) { while (sm_busy); // wait for bus to be free. sm_busy = 1; // occupy smbus (set to busy) smb0cn = 0x44; // smbus enabled, ack on acknowledge cycle byte_number = 2; // 2 address bytes command = (chip_select | read); // chip select + read
AN113 26 rev. 1.3 high_add = ((byte_address >> 8) & 0x00ff);// upper 8 address bits low_add = (byte_address & 0x00ff); // lower 8 address bits sto = 0; sta = 1; // start transfer while (sm_busy); // wait for transfer to finish return word; } //------------------------------------------------------------------------------------ // interrupt service routine //------------------------------------------------------------------------------------ // smbus interrupt service routine: void smbus_isr (void) interrupt 7 { switch (smb0sta){ // status code for the smbus (smb0sta register) // master transmitter/receiver: start condition transmitted. // the r/w bit of the command word sent after this state will // always be a zero (w) because for both read and write, // the memory address must be written first. case smb_start: smb0dat = (command & 0xfe); // load address of the slave to be accessed. sta = 0; // manually clear start bit break; // master transmitter/receiver: repeated start condition transmitted. // this state should only occur during a read, after the memory address has been // sent and acknowledged. case smb_rp_start: smb0dat = command; // command should hold slave address + r. sta = 0; break; // master transmitter: slave address + write transmitted. ack received. case smb_mtaddack: smb0dat = high_add; // load high byte of memory address // to be written. break; // master transmitter: slave address + write transmitted. nack received. // the slave is not responding. send a stop followed by a start to try again. case smb_mtaddnack: sto = 1; sta = 1; break; // master transmitter: data byte transmitted. ack received. // this state is used in both read and write operations. check byte_number // for memory address status - if only high_add has been sent, load low_add. // if low_add has been sent, check command for r/w value to determine // next state. case smb_mtdback: switch (byte_number){
AN113 rev. 1.3 27 case 2: // if byte_number=2, only high_add smb0dat = low_add; // has been sent. byte_number--; // decrement for next time around. break; case 1: // if byte_number=1, low_add was just sent. if (command & 0x01){ // if r/w=read, sent repeated start. sto = 0; sta = 1; } else { smb0dat = word; // if r/w=write, load byte to write. byte_number--; } break; default: // if byte_number=0, transfer is finished. sto = 1; sm_busy = 0; // free smbus } break; // master transmitter: data byte transmitted. nack received. // slave not responding. send stop followed by start to try again. case smb_mtdbnack: sto = 1; sta = 1; break; // master transmitter: arbitration lost. // should not occur. if so, restart transfer. case smb_mtarblost: sto = 1; sta = 1; break; // master receiver: slave address + read transmitted. ack received. // set to transmit nack after next transfer since it will be the last (only) // byte. case smb_mraddack: aa = 0; // nack sent on acknowledge cycle. break; // master receiver: slave address + read transmitted. nack received. // slave not responding. send repeated start to try again. case smb_mraddnack: sto = 0; sta = 1; break; // data byte received. ack transmitted. // state should not occur because aa is set to zero in previous state. // send stop if state does occur. case smb_mrdback: sto = 1; sm_busy = 0; break; // data byte received. nack transmitted. // read operation has completed. read data register and send stop.
AN113 28 rev. 1.3 case smb_mrdbnack: word = smb0dat; sto = 1; sm_busy = 0; // free smbus break; // all other status codes meaningless in this application. reset communication. default: sto = 1; // reset communication. sm_busy = 0; break; } si=0; // clear interrupt flag }
AN113 rev. 1.3 29 //------------------------------------------------------------------------------------ // // copyright 2001 cygnal integrated products, inc. // // file name : smb_ex3.c // target device : c8051f000 // created on : 2/20/01 // created by : js // // example code to demonstrate the use of the smbus interface between two cf000 devices. // the devices operate in a peer-to-peer configuration. // // demonstration includes use of op codes for each device to command the other to: // // 1) write a byte to dac0 // 2) write a byte to a data buffer // 3) perform an adc conversion // 4) read a byte from a data buffer // // these op codes are can be tested easily if each chip has dac0 routed to ain0. // with this configuration, a read_adc command can be used to test the output // of a write_dac command. // // code assumes that two cf0xx devices are connected via scl and sda, with // slave addresses (held by register smb0adr) // chip_a = 1111000 // chip_b = 1110000 // // test code is included. for testing purposes, the test code should be omitted // in one device, and run in the other. this can be accomplished by commenting // the op_code_handler() call before the test code in the device that will assume // the master role. // // please note that the constant my_add must correspond with the // current device - change it to chip_b when downloading code to chip_b. // //------------------------------------------------------------------------------------ //------------------------------------------------------------------------------------ // includes //------------------------------------------------------------------------------------ #include // sfr declarations //------------------------------------------------------------------------------------ // global constants //------------------------------------------------------------------------------------ #define write 0x00 // write direction bit #define read 0x01 // read direction bit // device addresses #define chip_a 0xf0 #define chip_b 0xe0 #define my_add chip_a // corresponds to the chip currently // being programmed. // peer-to-peer op_codes #define read_adc 0x01 // op_code to read from slave adc #define write_dac 0x02 // op_code to write to slave dac
AN113 30 rev. 1.3 #define write_buf 0x03 // op_code to write to slave buffer #define read_buf 0x04 // op_code to read from slave buffer //smbus states: // mt = master transmitter // mr = master receiver // st = slave transmitter // sr = slave receiver #define smb_bus_error 0x00 // (all modes) bus error #define smb_start 0x08 // (mt & mr) start transmitted #define smb_rp_start 0x10 // (mt & mr) repeated start #define smb_mtaddack 0x18 // (mt) slave address + w transmitted; // ack received #define smb_mtaddnack 0x20 // (mt) slave address + w transmitted; // nack received #define smb_mtdback 0x28 // (mt) data byte transmitted; ack rec?vd #define smb_mtdbnack 0x30 // (mt) data byte transmitted; nack rec?vd #define smb_mtarblost 0x38 // (mt) arbitration lost #define smb_mraddack 0x40 // (mr) slave address + r transmitted; // ack received #define smb_mraddnack 0x48 // (mr) slave address + r transmitted; // nack received #define smb_mrdback 0x50 // (mr) data byte rec?vd; ack transmitted #define smb_mrdbnack 0x58 // (mr) data byte rec?vd; nack transmitted #define smb_sroadack 0x60 // (sr) smb?s own slave address + w rec?vd; // ack transmitted #define smb_sroarblost 0x68 // (sr) smb?s own slave address + w rec?vd; // arbitration lost #define smb_srgadack 0x70 // (sr) general call address rec?vd; // ack transmitted #define smb_srgarblost 0x78 // (sr) arbitration lost when transmitting // slave addr + r/w as master; general // call address rec?vd; ack transmitted #define smb_srodback 0x80 // (sr) data byte received under own slave // address; ack returned #define smb_srodbnack 0x88 // (sr) data byte received under own slave // address; nack returned #define smb_srgdback 0x90 // (sr) data byte received under general // call address; ack returned #define smb_srgdbnack 0x98 // (sr) data byte received under general // call address; nack returned #define smb_srstop 0xa0 // (sr) stop or repeated start received // while addressed as a slave #define smb_stoadack 0xa8 // (st) smb?s own slave address + r rec?vd; // ack transmitted #define smb_stoarblost 0xb0 // (st) arbitration lost in transmitting // slave address + r/w as master; own // slave address rec?vd; ack transmitted #define smb_stdback 0xb8 // (st) data byte transmitted; ack rec?ed #define smb_stdbnack 0xc0 // (st) data byte transmitted; nack rec?ed #define smb_stdblast 0xc8 // (st) last data byte transmitted (aa=0); // ack received #define smb_sclhighto 0xd0 // (st & sr) scl clock high timer per // smb0cr timed out (fte=1) #define smb_idle 0xf8 // (all modes) idle //-----------------------------------------------------------------------------------
AN113 rev. 1.3 31 //global variables //----------------------------------------------------------------------------------- char command; // holds the slave address + r/w bit for // use in the smbus isr. char word; // holds data to be transmitted by the smbus // or data that has just been received. char op_code; // holds an op code to be sent or one // that has just been received. char lost_command, lost_word, lost_code; // used to hold relevant data after a // lost arbitration. char data_buf[16]; // data buffer accessed by op_code_handler bit lost; // arbitration lost flag, set when // arbitration is lost while in master mode. // used to resume a failed transfer. bit sm_busy; // this bit is set when a send or receive // is started. it is cleared by the // isr when the operation is finished. bit valid_op; // flag used to determine if byte received // as a slave is an op_code or data. bit data_ready; // used by op_code handler to flag when // valid data has been received from the // master //------------------------------------------------------------------------------------ // function prototypes //------------------------------------------------------------------------------------ void smbus_isr (void); char sla_read(char chip_select, char out_op); void sla_send(char chip_select, char out_op, char out_data); void op_code_handler(void); //------------------------------------------------------------------------------------ // main routine //------------------------------------------------------------------------------------ void main (void) { char i, check_1, check_2; // variables used for testing purposes only. wdtcn = 0xde; // disable watchdog timer wdtcn = 0xad; xbr0 = 0x01; // route smbus to gpio pins through crossbar xbr2 = 0x40; // enable crossbar and weak pull-ups smb0cn = 0x44; // enable smbus with acknowledge low (aa = 1) smb0cr = -80; // smbus clock rate = 100 khz smb0adr = my_add; // set own slave address.
AN113 32 rev. 1.3 adc0cn = 0x80; // enable adc, conversions to start with // write to adbusy. adc0cn |= 0x01; // adc data registers left-justified. dac0cn = 0x84; // enable dac0, with left justified data // registers. ref0cn = 0x03; // reference voltage enabled. eie1 |= 2; // smbus interrupt enable ea = 1; // global interrupt enable sm_busy = 0; // free bus for first transfer. si = 0; // // op_code_handler(); // this line should be commented in only // one of the two peer devices. it is // for testing purposes only. // in a normal setup, the op_code_handler // would be running at all times in order // to react to op_codes being sent to the // device. // test code-------------------------------------------------------------------------- // this code is used only to test the interface between the two devices. if // the above op_code_handler line is commented out, this device assumes the master // role. the other device should be running the op_code_handler at all times, to // respond to the op_codes below. sla_send(chip_b, (0x40 | write_buf), 0x24); // write to index 4 // in the data buffer sla_send(chip_b, (0x60 | write_buf), 0x25); // write to index 6 sla_send(chip_b, (0x80 | write_buf), 0x26); // write to index 8 sla_send(chip_b, (0x10 | write_buf), 0x27); // write to index 1 check_1 = sla_read(chip_b, (0x40 | read_buf)); // read index 4 from the buffer check_1 = sla_read(chip_b, (0x60 | read_buf)); // read index 6 check_1 = sla_read(chip_b, (0x80 | read_buf)); // read index 8 check_1 = sla_read(chip_b, (0x10 | read_buf)); // read index 1 // loop to continuously increase the dac output on chip_b, and read its // adc each round. dac output on chip_b should ramp. for (i=0;i<50;i++){ sla_send(chip_b, write_dac, 2*i); // write 2*i to dac0 on chip_b check_1 = sla_read(chip_b, read_adc); // read ain0 on chip_b check_2 = 2*i;} // check_1 should be approximately // the same as check_2. // end test code---------------------------------------------------------------------- } //------------------------------------------------------------------------------------ // functions //------------------------------------------------------------------------------------ // send to slave.
AN113 rev. 1.3 33 // the send function transmits two bytes to the slave device: an op code, and a data // byte. there are two op code choices for sending data: write_dac and write_buf. // if the op code is write_buf, then the upper 4 bits of the op code should contain // the buffer index. for example, to write to index 2 of the data buffer, the // op_code parameter should be (0x20 | write_buf). // // chip_select = address of slave device. // out_op = op_code to be sent. // out_data = data byte to be sent. void sla_send(char chip_select, char out_op, char out_data){ while(sm_busy); // wait while smbus is busy. sm_busy = 1; // smbus busy flag set. smb0cn = 0x44; // smbus enabled, ack low. command = (chip_select | write); // command = 7 address bits + write. op_code = out_op; // word = op_code to be transmitted. word = out_data; // data = data to be transmitted. sto = 0; sta = 1; // start transfer. } // read from slave. // the read function transmits a 1-byte op code, then issues a repeated start // to request a 1-byte read. the two op code choices are read_adc and read_buf. // if the op code is read_buf, then the upper 4 bits of the op code should // contain the buffer index. for example, to read index 5 of the data buffer, // the op code should be (0x50 | read_buf). // // chip_select = address of slave device. // out_op = op_code to be sent. char sla_read(char chip_select, char out_op){ while(sm_busy); // wait while smbus is busy. sm_busy = 1; // set busy flag. smb0cn = 0x44; // enable smbus, ack low. command = (chip_select | read); // command = 7 address bits + read op_code = out_op; sto = 0; sta = 1; // start transfer. while(sm_busy); // wait for transfer to finish. return word; // return received word. } // op_code handler. // decodes incoming op codes and performs tasks according to those op codes. // a call to this function runs forever. // // the valid_op bit flags when a valid op code has been received. upon receipt, // the handler decodes the op code, performs the task, then clears // valid_op to wait for another code. void op_code_handler(void){ char index; // data buffer index while (1){ // run forever valid_op = 0; // wait for a valid op_code while (!valid_op); //
AN113 34 rev. 1.3 // the lower 4 bits of the op_code are used to determine the action, while the // upper 4 bits are used to index the data_buf array when the read_buf or // write_buf op_codes are received. note that the smbus is stalled until the // op_code is decoded. switch (op_code & 0x0f){ // decode op_code // op_code = read_adc - perform an adc conversion, and place data in // output buffer. // read only adc high byte. case read_adc: si = 0; // free the bus aa = 0; // take slave ?offline? adcint = 0; // clear adc interrupt flag. adbusy = 1; // start conversion. while (!adcint); // wait for conversion to finish. word = adc0h; // put data in output buffer. aa = 1; // put slave back ?online? valid_op = 0; // look for a new op_code break; // op_code = write_dac - wait for a valid data byte, and write it to high // byte of dac0. case write_dac: si = 0; // free the bus data_ready = 0; // wait for valid data. while (!data_ready); // dac0l = 0; // dac low byte dac0h = word; // dac high byte valid_op = 0; // look for new op_code si = 0; // free bus when finished. break; // op_code = write_buf - wait for valid data byte, then place data in // data_buf array. index data according to upper 4 bits of op_code. case write_buf: si = 0; // free the bus index = (op_code & 0xf0); // use upper 4 bits as array index. data_ready = 0; // wait for valid data. while (!data_ready); // data_buf[index] = word; // store data in array. valid_op = 0; // look for new op_code si = 0; // free the bus when finished. break; // op_code = read_buf - read data_buf array and place byte in output buffer. // array index determined by upper 4 bits of op_code. case read_buf: index = (op_code & 0xf0); // use upper 4 bits as array index. word = data_buf[index]; // place indexed data in output buffer. valid_op = 0; // look for new op_code si = 0; // free the bus when finished. break; } if (lost){ // if lost is set, the device has recently command = lost_command; // lost an arbitration. load saved values word = lost_word; // back into transfer variables, and retry op_code = lost_code; // transfer.
AN113 rev. 1.3 35 lost = 0; sto = 0; sta = 1; } } } //------------------------------------------------------------------------------------ // smbus interrupt service routine //------------------------------------------------------------------------------------ void smbus_isr (void) interrupt 7 { switch (smb0sta){ // status code for the smbus // (smb0sta register) // master transmitter/receiver: start condition transmitted. // load smb0dat with slave device address. mask out r/w bit since all transfers // start with an op_code write. case smb_start: smb0dat = (command & 0xfe); // load address of the slave to be accessed. // mask out r/w bit because first transfer // will always be a write of the op_code. sta = 0; // manually clear sta bit si = 0; // clear interrupt flag break; // master transmitter/receiver: repeated start condition transmitted. // this state only occurs during a read, after the op_code has been sent. load // device address + read into smb0dat. case smb_rp_start: smb0dat = command; sta = 0; // manually clear start bit. si = 0; break; // master transmitter: slave address + write transmitted. ack received. // load op_code into smb0dat. case smb_mtaddack: smb0dat = op_code; si = 0; // clear interrupt flag break; // master transmitter: slave address + write transmitted. nack received. // the slave is not responding. use ack polling to retry. case smb_mtaddnack: sto = 1; sta = 1; si = 0; // clear interrupt flag break; // master transmitter: data byte transmitted. ack received. // check op_code - if it is a read code, send repeated start to begin // read. if it is a write code, load word into smb0dat for transfer. // if it is not a valid code, then either 1) data has been transmitted // and the transfer is finished, or 2) there is an error. in either case, // send stop and end transfer.
AN113 36 rev. 1.3 case smb_mtdback: switch (op_code & 0x0f){ // check only lower 4 bits. // op_code is a read. send repeated start. case read_buf: case read_adc: op_code = 0; // current op_code no longer useful sto = 0; sta = 1; break; // op_code is a write. load output data into smb0dat. case write_buf: case write_dac: smb0dat = word; op_code = 0; // clear op_code so transfer ends the next break; // time this state occurs // (after data is sent). default: // no valid op_code. end transfer. sto = 1; sm_busy = 0; break; } si = 0; break; // master transmitter: data byte transmitter. nack received. // use ack polling to retry transfer. case smb_mtdbnack: sto = 1; sta = 1; si = 0; // clear interrupt flag break; // master transmitter: arbitration lost. case smb_mtarblost: lost_command = command; // lost_word = word; // store variables for use when bus is free. lost_code = op_code; // lost = 1; // set flag to retry transfer // when bus is free. si = 0; // clear interrupt flag break; // master receiver: slave address + read transmitted. ack received. // set to transmit nack after next transfer since it will be the // last (only) byte. case smb_mraddack: aa = 0; // nack sent during acknowledge cycle. si = 0; break; // master receiver: slave address + read transmitted. nack received. // slave not responding. send repeated start to try again. case smb_mraddnack: sto = 0; sta = 1;
AN113 rev. 1.3 37 si = 0; break; // master receiver: data byte received. ack transmitted. // state should not occur because aa is cleared in previous state. // send stop if state does occur. case smb_mrdback: sto = 1; sm_busy = 0; si = 0; break; // master receiver: data byte received. nack transmitted. // read operation has completed. read data register and send stop. case smb_mrdbnack: word = smb0dat; sto = 1; sm_busy = 0; aa = 1; // set aa for next transfer si = 0; break; // slave receiver: arbitration lost, general call address received. // set lost flag to retry transfer when bus is free. fall through. case smb_srgarblost: // slave receiver: arbitration lost, own slave address + write received. // set lost flag to retry transfer when bus is free. // set sto bit to get out of master mode. case smb_sroarblost: lost_command = command; // lost_word = word; // store variables for use when bus is free. lost_code = op_code; // lost = 1; // retry transfer when bus is free. si = 0; break; // slave receiver: slave address + write received. ack transmitted. // fall through. case smb_sroadack: // slave receiver: general call address received. ack transmitted. case smb_srgadack: si = 0; break; // slave receiver: data byte received after addressed by general // call address + write. // ack transmitted. fall through. case smb_srgdback: // slave receiver: data byte received after addressed by own // slave address + write. // ack transmitted. // take action depending on op_code or data received. case smb_srodback: if (!valid_op){ // if valid_op=0, this byte is an op_code. op_code = smb0dat; // store op_code valid_op = 1; // next byte is not an op_code
AN113 38 rev. 1.3 } else { data_ready = 1; // valid data has been received. process // in op_code handler. word = smb0dat; si = 0; } break; // slave receiver: data byte received while addressed as slave. // nack transmitted. should not occur since aa will not be cleared // as slave. fall through to next state. case smb_srodbnack: // slave receiver: data byte received while addressed by general call. // nack transmitted. // should not occur since aa will not be cleared as slave. case smb_srgdbnack: aa = 1; si = 0; break; // slave receiver: stop or repeated start received while addressed as slave. case smb_srstop: si = 0; break; // slave transmitter: own slave address + read received. ack transmitted. // load smb0dat with data to be output. case smb_stoadack: smb0dat = word; si = 0; break; // slave transmitter: arbitration lost as master. own address + read received. // ack transmitted. case smb_stoarblost: lost_command = command; // lost_word = word; // store variables for use when bus lost_code = op_code; // is free. lost = 1; // retry when bus is free. si = 0; break; // slave transmitter: data byte transmitted. ack received. fall through. case smb_stdback: // slave transmitter: data byte transmitted. nack received. fall through. case smb_stdbnack: // slave transmitter: last data byte transmitted. ack received. // no action necessary. case smb_stdblast: si = 0; break; // all other status codes invalid. reset communication. default: sto = 1;
AN113 rev. 1.3 39 sm_busy = 0; break; } }
AN113 40 rev. 1.3 software example for the c8051f02x series //------------------------------------------------------------------------------------ // // copyright 2001 cygnal integrated products, inc. // // file name : smb_ex3.c // target device : c8051f020 // created on : 6/5/02 // created by : js / fb // // example code to demonstrate the use of the smbus interface between two cf000 devices. // the devices operate in a peer-to-peer configuration. // // demonstration includes use of op codes for each device to command the other to: // // 1) write a byte to dac0 // 2) write a byte to a data buffer // 3) perform an adc conversion // 4) read a byte from a data buffer // // these op codes are can be tested easily if each chip has dac0 routed to ain0. // with this configuration, a read_adc command can be used to test the output // of a write_dac command. // // code assumes that two cf0xx devices are connected via scl and sda, with // slave addresses (held by register smb0adr) // chip_a = 1111000 // chip_b = 1110000 // // test code is included. for testing purposes, the test code should be omitted // in one device, and run in the other. this can be accomplished by commenting // the op_code_handler() call before the test code in the device that will assume // the master role. // // please note that the constant my_add must correspond with the // current device - change it to chip_b when downloading code to chip_b. // //------------------------------------------------------------------------------------ //----------------------------------------------------------------------------- // includes //----------------------------------------------------------------------------- #include // sfr declarations //----------------------------------------------------------------------------- // 16-bit sfr definitions for ?f02x //----------------------------------------------------------------------------- sfr16 dp = 0x82; // data pointer sfr16 tmr3rl = 0x92; // timer3 reload value sfr16 tmr3 = 0x94; // timer3 counter sfr16 adc0 = 0xbe; // adc0 data sfr16 adc0gt = 0xc4; // adc0 greater than window sfr16 adc0lt = 0xc6; // adc0 less than window sfr16 rcap2 = 0xca; // timer2 capture/reload sfr16 t2 = 0xcc; // timer2 sfr16 rcap4 = 0xe4; // timer4 capture/reload
AN113 rev. 1.3 41 sfr16 t4 = 0xf4; // timer4 sfr16 dac0 = 0xd2; // dac0 data sfr16 dac1 = 0xd5; // dac1 data //------------------------------------------------------------------------------------ // global constants //------------------------------------------------------------------------------------ #define write 0x00 // write direction bit #define read 0x01 // read direction bit // device addresses #define chip_a 0xf0 #define chip_b 0xe0 #define my_add chip_a // corresponds to the chip currently // being programmed. // peer-to-peer op_codes #define read_adc 0x01 // op_code to read from slave adc #define write_dac 0x02 // op_code to write to slave dac #define write_buf 0x03 // op_code to write to slave buffer #define read_buf 0x04 // op_code to read from slave buffer //smbus states: // mt = master transmitter // mr = master receiver // st = slave transmitter // sr = slave receiver #define smb_bus_error 0x00 // (all modes) bus error #define smb_start 0x08 // (mt & mr) start transmitted #define smb_rp_start 0x10 // (mt & mr) repeated start #define smb_mtaddack 0x18 // (mt) slave address + w transmitted; // ack received #define smb_mtaddnack 0x20 // (mt) slave address + w transmitted; // nack received #define smb_mtdback 0x28 // (mt) data byte transmitted; ack rec?vd #define smb_mtdbnack 0x30 // (mt) data byte transmitted; nack rec?vd #define smb_mtarblost 0x38 // (mt) arbitration lost #define smb_mraddack 0x40 // (mr) slave address + r transmitted; // ack received #define smb_mraddnack 0x48 // (mr) slave address + r transmitted; // nack received #define smb_mrdback 0x50 // (mr) data byte rec?vd; ack transmitted #define smb_mrdbnack 0x58 // (mr) data byte rec?vd; nack transmitted #define smb_sroadack 0x60 // (sr) smb?s own slave address + w rec?vd; // ack transmitted #define smb_sroarblost 0x68 // (sr) smb?s own slave address + w rec?vd; // arbitration lost #define smb_srgadack 0x70 // (sr) general call address rec?vd; // ack transmitted #define smb_srgarblost 0x78 // (sr) arbitration lost when transmitting // slave addr + r/w as master; general // call address rec?vd; ack transmitted #define smb_srodback 0x80 // (sr) data byte received under own slave // address; ack returned #define smb_srodbnack 0x88 // (sr) data byte received under own slave // address; nack returned #define smb_srgdback 0x90 // (sr) data byte received under general
AN113 42 rev. 1.3 // call address; ack returned #define smb_srgdbnack 0x98 // (sr) data byte received under general // call address; nack returned #define smb_srstop 0xa0 // (sr) stop or repeated start received // while addressed as a slave #define smb_stoadack 0xa8 // (st) smb?s own slave address + r rec?vd; // ack transmitted #define smb_stoarblost 0xb0 // (st) arbitration lost in transmitting // slave address + r/w as master; own // slave address rec?vd; ack transmitted #define smb_stdback 0xb8 // (st) data byte transmitted; ack rec?ed #define smb_stdbnack 0xc0 // (st) data byte transmitted; nack rec?ed #define smb_stdblast 0xc8 // (st) last data byte transmitted (aa=0); // ack received #define smb_sclhighto 0xd0 // (st & sr) scl clock high timer per // smb0cr timed out (fte=1) #define smb_idle 0xf8 // (all modes) idle //----------------------------------------------------------------------------------- //global variables //----------------------------------------------------------------------------------- char command; // holds the slave address + r/w bit for // use in the smbus isr. char word; // holds data to be transmitted by the smbus // or data that has just been received. char op_code; // holds an op code to be sent or one // that has just been received. char lost_command, lost_word, lost_code; // used to hold relevant data after a // lost arbitration. char data_buf[16]; // data buffer accessed by op_code_handler bit lost; // arbitration lost flag, set when // arbitration is lost while in master mode. // used to resume a failed transfer. bit sm_busy; // this bit is set when a send or receive // is started. it is cleared by the // isr when the operation is finished. bit valid_op; // flag used to determine if byte received // as a slave is an op_code or data. bit data_ready; // used by op_code handler to flag when // valid data has been received from the // master //------------------------------------------------------------------------------------ // function prototypes //------------------------------------------------------------------------------------ void sysclk_init (void); void smbus_isr (void);
AN113 rev. 1.3 43 char sla_read(char chip_select, char out_op); void sla_send(char chip_select, char out_op, char out_data); void op_code_handler(void); //------------------------------------------------------------------------------------ // main routine //------------------------------------------------------------------------------------ void main (void) { char i, check_1, check_2; // variables used for testing purposes only. wdtcn = 0xde; // disable watchdog timer wdtcn = 0xad; sysclk_init(); // turn on the external oscillator xbr0 = 0x01; // route smbus to gpio pins through crossbar xbr2 = 0x40; // enable crossbar and weak pull-ups smb0cn = 0x44; // enable smbus with acknowledge low (aa = 1) smb0cr = -80; // smbus clock rate = 100 khz smb0adr = my_add; // set own slave address. adc0cn = 0x80; // enable adc, conversions to start with // write to ad0busy. adc0cn |= 0x01; // adc data registers left-justified. dac0cn = 0x84; // enable dac0, with left justified data // registers. ref0cn = 0x03; // reference voltage enabled. eie1 |= 2; // smbus interrupt enable ea = 1; // global interrupt enable sm_busy = 0; // free bus for first transfer. si = 0; // // op_code_handler(); // this line should be commented in only // one of the two peer devices. it is // for testing purposes only. // in a normal setup, the op_code_handler // would be running at all times in order // to react to op_codes being sent to the // device. // test code-------------------------------------------------------------------------- // this code is used only to test the interface between the two devices. if // the above op_code_handler line is commented out, this device assumes the master // role. the other device should be running the op_code_handler at all times, to // respond to the op_codes below. sla_send(chip_b, (0x40 | write_buf), 0x24); // write to index 4 // in the data buffer sla_send(chip_b, (0x60 | write_buf), 0x25); // write to index 6 sla_send(chip_b, (0x80 | write_buf), 0x26); // write to index 8 sla_send(chip_b, (0x10 | write_buf), 0x27); // write to index 1
AN113 44 rev. 1.3 check_1 = sla_read(chip_b, (0x40 | read_buf)); // read index 4 from the buffer check_1 = sla_read(chip_b, (0x60 | read_buf)); // read index 6 check_1 = sla_read(chip_b, (0x80 | read_buf)); // read index 8 check_1 = sla_read(chip_b, (0x10 | read_buf)); // read index 1 // loop to continuously increase the dac output on chip_b, and read its // adc each round. dac output on chip_b should ramp. for (i=0;i<50;i++){ sla_send(chip_b, write_dac, 2*i); // write 2*i to dac0 on chip_b check_1 = sla_read(chip_b, read_adc); // read ain0 on chip_b check_2 = 2*i; // check_1 should be approximately } // the same as check_2. // end test code---------------------------------------------------------------------- } //------------------------------------------------------------------------------------ // initialization routines //------------------------------------------------------------------------------------ //----------------------------------------------------------------------------- // sysclk_init //----------------------------------------------------------------------------- // // this routine initializes the system clock to use an 22.1184mhz crystal // as its clock source. // void sysclk_init (void) { int i; // delay counter oscxcn = 0x67; // start external oscillator with // 22.1184mhz crystal for (i=0; i < 256; i++) ; // xtlvld blanking interval (>1ms) while (!(oscxcn & 0x80)) ; // wait for crystal osc. to settle oscicn = 0x88; // select external oscillator as sysclk // source and enable missing clock // detector } //------------------------------------------------------------------------------------ // functions //------------------------------------------------------------------------------------ // send to slave. // the send function transmits two bytes to the slave device: an op code, and a data // byte. there are two op code choices for sending data: write_dac and write_buf. // if the op code is write_buf, then the upper 4 bits of the op code should contain // the buffer index. for example, to write to index 2 of the data buffer, the // op_code parameter should be (0x20 | write_buf). // // chip_select = address of slave device.
AN113 rev. 1.3 45 // out_op = op_code to be sent. // out_data = data byte to be sent. void sla_send(char chip_select, char out_op, char out_data){ while(sm_busy); // wait while smbus is busy. sm_busy = 1; // smbus busy flag set. smb0cn = 0x44; // smbus enabled, ack low. command = (chip_select | write); // command = 7 address bits + write. op_code = out_op; // word = op_code to be transmitted. word = out_data; // data = data to be transmitted. sto = 0; sta = 1; // start transfer. } // read from slave. // the read function transmits a 1-byte op code, then issues a repeated start // to request a 1-byte read. the two op code choices are read_adc and read_buf. // if the op code is read_buf, then the upper 4 bits of the op code should // contain the buffer index. for example, to read index 5 of the data buffer, // the op code should be (0x50 | read_buf). // // chip_select = address of slave device. // out_op = op_code to be sent. char sla_read(char chip_select, char out_op){ while(sm_busy); // wait while smbus is busy. sm_busy = 1; // set busy flag. smb0cn = 0x44; // enable smbus, ack low. command = (chip_select | read); // command = 7 address bits + read op_code = out_op; sto = 0; sta = 1; // start transfer. while(sm_busy); // wait for transfer to finish. return word; // return received word. } // op_code handler. // decodes incoming op codes and performs tasks according to those op codes. // a call to this function runs forever. // // the valid_op bit flags when a valid op code has been received. upon receipt, // the handler decodes the op code, performs the task, then clears // valid_op to wait for another code. void op_code_handler(void){ char index; // data buffer index while (1){ // run forever valid_op = 0; // wait for a valid op_code while (!valid_op); // // the lower 4 bits of the op_code are used to determine the action, while the // upper 4 bits are used to index the data_buf array when the read_buf or // write_buf op_codes are received. note that the smbus is stalled until the // op_code is decoded. switch (op_code & 0x0f){ // decode op_code
AN113 46 rev. 1.3 // op_code = read_adc - perform an adc conversion, and place data in // output buffer. // read only adc high byte. case read_adc: si = 0; // free the bus aa = 0; // take slave ?offline? ad0int = 0; // clear adc interrupt flag. ad0busy = 1; // start conversion. while (!ad0int); // wait for conversion to finish. word = adc0h; // put data in output buffer. aa = 1; // put slave back ?online? valid_op = 0; // look for a new op_code break; // op_code = write_dac - wait for a valid data byte, and write it to high // byte of dac0. case write_dac: si = 0; // free the bus data_ready = 0; // wait for valid data. while (!data_ready); // dac0l = 0; // dac low byte dac0h = word; // dac high byte valid_op = 0; // look for new op_code si = 0; // free bus when finished. break; // op_code = write_buf - wait for valid data byte, then place data in // data_buf array. index data according to upper 4 bits of op_code. case write_buf: si = 0; // free the bus index = (op_code & 0xf0); // use upper 4 bits as array index. data_ready = 0; // wait for valid data. while (!data_ready); // data_buf[index] = word; // store data in array. valid_op = 0; // look for new op_code si = 0; // free the bus when finished. break; // op_code = read_buf - read data_buf array and place byte in output buffer. // array index determined by upper 4 bits of op_code. case read_buf: index = (op_code & 0xf0); // use upper 4 bits as array index. word = data_buf[index]; // place indexed data in output buffer. valid_op = 0; // look for new op_code si = 0; // free the bus when finished. break; } if (lost){ // if lost is set, the device has recently command = lost_command; // lost an arbitration. load saved values word = lost_word; // back into transfer variables, and retry op_code = lost_code; // transfer. lost = 0; sto = 0; sta = 1; } } }
AN113 rev. 1.3 47 //------------------------------------------------------------------------------------ // smbus interrupt service routine //------------------------------------------------------------------------------------ void smbus_isr (void) interrupt 7 { switch (smb0sta){ // status code for the smbus // (smb0sta register) // master transmitter/receiver: start condition transmitted. // load smb0dat with slave device address. mask out r/w bit since all transfers // start with an op_code write. case smb_start: smb0dat = (command & 0xfe); // load address of the slave to be accessed. // mask out r/w bit because first transfer // will always be a write of the op_code. sta = 0; // manually clear sta bit si = 0; // clear interrupt flag break; // master transmitter/receiver: repeated start condition transmitted. // this state only occurs during a read, after the op_code has been sent. load // device address + read into smb0dat. case smb_rp_start: smb0dat = command; sta = 0; // manually clear start bit. si = 0; break; // master transmitter: slave address + write transmitted. ack received. // load op_code into smb0dat. case smb_mtaddack: smb0dat = op_code; si = 0; // clear interrupt flag break; // master transmitter: slave address + write transmitted. nack received. // the slave is not responding. use ack polling to retry. case smb_mtaddnack: sto = 1; sta = 1; si = 0; // clear interrupt flag break; // master transmitter: data byte transmitted. ack received. // check op_code - if it is a read code, send repeated start to begin // read. if it is a write code, load word into smb0dat for transfer. // if it is not a valid code, then either 1) data has been transmitted // and the transfer is finished, or 2) there is an error. in either case, // send stop and end transfer. case smb_mtdback: switch (op_code & 0x0f){ // check only lower 4 bits. // op_code is a read. send repeated start. case read_buf: case read_adc: op_code = 0; // current op_code no longer useful
AN113 48 rev. 1.3 sto = 0; sta = 1; break; // op_code is a write. load output data into smb0dat. case write_buf: case write_dac: smb0dat = word; op_code = 0; // clear op_code so transfer ends the next break; // time this state occurs // (after data is sent). default: // no valid op_code. end transfer. sto = 1; sm_busy = 0; break; } si = 0; break; // master transmitter: data byte transmitter. nack received. // use ack polling to retry transfer. case smb_mtdbnack: sto = 1; sta = 1; si = 0; // clear interrupt flag break; // master transmitter: arbitration lost. case smb_mtarblost: lost_command = command; // lost_word = word; // store variables for use when bus is free. lost_code = op_code; // lost = 1; // set flag to retry transfer // when bus is free. si = 0; // clear interrupt flag break; // master receiver: slave address + read transmitted. ack received. // set to transmit nack after next transfer since it will be the // last (only) byte. case smb_mraddack: aa = 0; // nack sent during acknowledge cycle. si = 0; break; // master receiver: slave address + read transmitted. nack received. // slave not responding. send repeated start to try again. case smb_mraddnack: sto = 0; sta = 1; si = 0; break; // master receiver: data byte received. ack transmitted. // state should not occur because aa is cleared in previous state. // send stop if state does occur. case smb_mrdback:
AN113 rev. 1.3 49 sto = 1; sm_busy = 0; si = 0; break; // master receiver: data byte received. nack transmitted. // read operation has completed. read data register and send stop. case smb_mrdbnack: word = smb0dat; sto = 1; sm_busy = 0; aa = 1; // set aa for next transfer si = 0; break; // slave receiver: arbitration lost, general call address received. // set lost flag to retry transfer when bus is free. fall through. case smb_srgarblost: // slave receiver: arbitration lost, own slave address + write received. // set lost flag to retry transfer when bus is free. // set sto bit to get out of master mode. case smb_sroarblost: lost_command = command; // lost_word = word; // store variables for use when bus is free. lost_code = op_code; // lost = 1; // retry transfer when bus is free. si = 0; break; // slave receiver: slave address + write received. ack transmitted. // fall through. case smb_sroadack: // slave receiver: general call address received. ack transmitted. case smb_srgadack: si = 0; break; // slave receiver: data byte received after addressed by general // call address + write. // ack transmitted. fall through. case smb_srgdback: // slave receiver: data byte received after addressed by own // slave address + write. // ack transmitted. // take action depending on op_code or data received. case smb_srodback: if (!valid_op){ // if valid_op=0, this byte is an op_code. op_code = smb0dat; // store op_code valid_op = 1; // next byte is not an op_code } else { data_ready = 1; // valid data has been received. process // in op_code handler. word = smb0dat; si = 0; } break;
AN113 50 rev. 1.3 // slave receiver: data byte received while addressed as slave. // nack transmitted. should not occur since aa will not be cleared // as slave. fall through to next state. case smb_srodbnack: // slave receiver: data byte received while addressed by general call. // nack transmitted. // should not occur since aa will not be cleared as slave. case smb_srgdbnack: aa = 1; si = 0; break; // slave receiver: stop or repeated start received while addressed as slave. case smb_srstop: si = 0; break; // slave transmitter: own slave address + read received. ack transmitted. // load smb0dat with data to be output. case smb_stoadack: smb0dat = word; si = 0; break; // slave transmitter: arbitration lost as master. own address + read received. // ack transmitted. case smb_stoarblost: lost_command = command; // lost_word = word; // store variables for use when bus lost_code = op_code; // is free. lost = 1; // retry when bus is free. si = 0; break; // slave transmitter: data byte transmitted. ack received. fall through. case smb_stdback: // slave transmitter: data byte transmitted. nack received. fall through. case smb_stdbnack: // slave transmitter: last data byte transmitted. ack received. // no action necessary. case smb_stdblast: si = 0; break; // all other status codes invalid. reset communication. default: sto = 1; sm_busy = 0; break; } }
AN113 rev. 1.3 51 notes:
AN113 52 rev. 1.3 contact information silicon laboratories inc. 4635 boston lane austin, tx 78735 tel: 1+(512) 416-8500 fax: 1+(512) 416-9669 toll free: 1+(877) 444-3032 email: productinfo@silabs.com internet: www.silabs.com silicon laboratories and silicon labs are trademarks of silicon laboratories inc. other products or brandnames mentioned herein are trademarks or registered trademarks of their respective holders. the information in this document is believed to be accurate in all respects at the time of publ ication but is subject to change without notice. silicon laboratories assumes no responsibility for errors and om issions, and disclaims responsibi lity for any consequences resu lting from the use of information included herein. ad ditionally, silicon laboratorie s assumes no responsibility for the functioning of und escribed features or parameters. silicon laboratories reserves the right to make changes without further notice . silicon laboratories makes no wa rranty, rep- resentation or guarantee regarding the suitability of its products for any particular purpose, nor does silicon laboratories as sume any liability arising out of the application or use of any product or circuit, and s pecifically disclaims any and all liability, including wi thout limitation conse- quential or incidental damages. silicon laborat ories products are not designed, intended, or authorized for use in applications intended to support or sustain life, or for any other application in which the failure of t he silicon laboratories product could create a s ituation where per- sonal injury or death may occur. should buyer purchase or us e silicon laboratories products for any such unintended or unauthor ized ap- plication, buyer shall indemnify and hold silicon laboratories harmles s against all claims and damages.


▲Up To Search▲   

 
Price & Availability of AN113

All Rights Reserved © IC-ON-LINE 2003 - 2022  

[Add Bookmark] [Contact Us] [Link exchange] [Privacy policy]
Mirror Sites :  [www.datasheet.hk]   [www.maxim4u.com]  [www.ic-on-line.cn] [www.ic-on-line.com] [www.ic-on-line.net] [www.alldatasheet.com.cn] [www.gdcy.com]  [www.gdcy.net]


 . . . . .
  We use cookies to deliver the best possible web experience and assist with our advertising efforts. By continuing to use this site, you consent to the use of cookies. For more information on cookies, please take a look at our Privacy Policy. X